/************************************
 * Package: RGcpp					*
 * Authors: Rui Eduardo Gouveia Gil	*
 * Date: 2010						*
 ************************************/
#ifndef __RGCPP_PROFILER_H__
#define __RGCPP_PROFILER_H__

#include <vector>
#include <fstream>
#include <iostream>
#include <boost/thread.hpp>

#include "rgcpp/structs/Map.h"
#include "rgcpp/structs/Types.h"
#include "rgcpp/structs/Singleton.h"

#define NANOSECONDS_PER_SECOND 1000000000

namespace rgcpp
{

class Profiler : public Singleton<Profiler>
{
	friend class Singleton<Profiler>;

	private:
		boost::xtime stime;

		boost::mutex mutThread;
		double threadsTimeTotal;
		Map<std::string,boost::xtime> threadTimeAux;
		Map<std::string,Types<double,double> > threadTime;

		boost::mutex mutModule;
		double modulesTimeTotal;
		Map<std::string,boost::xtime> moduleTimeAux;
		Map<std::string,Types<double,unsigned int,double> > moduleTime;

		bool running;
		double seconds;
		std::string filePath;
		boost::thread* mThread;
		boost::mutex initMutex;

	public:
		~Profiler();

		double getTotalProfileTime();

		void regThreadTime(const std::string& thread);
		void beginThreadTime(const std::string& thread);
		Types<double,double,double> getThreadTime(const std::string& thread);					//total,maxtime,percentage
		std::vector<Types<std::string,double,double,double> > getThreadsTime();		//thread,total,maxtime,percentage
		double getThreadsTimeTotal();
		void endThreadTime(const std::string& thread);

		void regModuleTime(const std::string& module);
		void beginModuleTime(const std::string& module);
		Types<double,unsigned int,double,double,double> getModuleTime(const std::string& module);					//total,count,maxtime,average,percentage
		std::vector<Types<std::string,double,unsigned int,double,double,double> > getModulesTime();		//module,count,total,maxtime,average,percentage
		double getModulesTimeTotal();
		void endModuleTime(const std::string& module);

		void print();
		std::string generateString();
		void writeToFile(const std::string& filePath);

		void startWriteToFile(const std::string& filePath, double seconds);
		void stopWriteToFile();

		void operator()();

		static boost::xtime getCurrentTime();

		static boost::xtime calculateTimeInterval(boost::xtime stime, boost::xtime etime);
		static void appendTimeInterval(boost::xtime stime, boost::xtime etime, boost::xtime& rtime);

		static double convertTimeToSeconds(boost::xtime rtime);
		static unsigned long convertTimeToMilliseconds(boost::xtime rtime);

		static boost::xtime convertTimeToXTime(double time);
		static boost::xtime convertTimeToXTime(unsigned long time);

		static boost::xtime appendTime(boost::xtime& rtime, boost::xtime atime);
		static boost::xtime appendTimeInSeconds(boost::xtime& rtime, double atime);

	private:
		Profiler();
};

}

#endif
